ArchR takes as input aligned BAM or fragment files. These files are stored in the HDF5 file format (hierarchical data format version5). The HDF5 files are the constituent pieces of an ArchR analysis. They are called Arrow files. All Arrow files are grouped into a project, a compressed R data file. The Files are accessed in minimal chunks (parallel read and write operations). Therefore, in memory we do not have any large file sizes.
To select high quality cells TSS enrichment scores are used.
First steps in ArchR
Load libraries
library(ArchR)
library(knitr)
library(rhdf5)
library(uwot)
library(tidyverse)
#library(caret)
h5disableFileLocking()
inputFiles <- getTutorialData("Hematopoiesis")
addArchRGenome("hg19")
Create Arrow Files
- read accessible fragments
- calculate QC information for each cell (TSS enrichment scores and nucelosome info)
- filter cells based on QC parameters
- create genome-wide tile matrix using 500-bp bins
- create GeneScoreMatrix using gene annotation
ArrowFiles <- createArrowFiles(
inputFiles = inputFiles,
sampleNames = names(inputFiles),
minTSS = 4, #Dont set this too high because you can always increase later
minFrags = 1000, # minimum number of mapped fragments required
# count matrix, instead of using peak it uses fixed-width sliding window of bins across the whole genome
addTileMat = TRUE,
addGeneScoreMat = TRUE, # uses signal proximal to the TSS to estimate gene activity
subThreading = FALSE,
maxFrags = 1e+05,
minFragSize = 10,
maxFragSize = 2000,
QCDir = "QualityControl",
# the length in bp that wraps around nucleosomes ->
#identify fragments as sub-nucleosome spanning, mono-nucleosome spanning or multi-nucleosome spanning
nucLength = 147,
# integer vector -> define region up/downstream of TSS to include as promoter region
# can be used to calculate e.g fraction of reads in promoter (FIP)
promoterRegion = c(2000, 100),
# parameters for computing TSS enrichment scores, window (bp) centered at TSS = 101
# flanking window = 2000 bp
# norm = size of flank window used for normalization = 100 bp
# accessibility within 101 bp surrounding the TSS will be normalized to accessibility
# in 100 bp bins from -2000:-1901bp and 1901: 2000
TSSParams = list(101, 2000, 100),
# which chromosomes to exclude form downstream analysis
# in human and mouse: mitochondrial DNA (chrM) and male sex chromosome (chrY)
# the fragments are still stored in the arrow files
excludeChr = c("chrM", "chrY"),
# number of chunks to divide chromosomes in -> low-memory parallelized reading of input files
nChunk = 5,
# name of field in input bam file containing the barcode tag information
bcTag = "qname",
offsetPlus = 4, # offset applied to + stranded Tn5 insertion -> account for precise Tn5 binding site
offsetMinus = -5,
logFile = createLogFile("createArrows")
)
Quality Control
Have a look at this for additional QC! https://bioconductor.org/packages/devel/bioc/vignettes/ATACseqQC/inst/doc/ATACseqQC.html
- the number of unique nuclear fragments (as opposed to mitochondrial fragments) A cell with very few usable fragments will not provide enough data to mak useful conclusions.
- signal-to-background ratio -> if this is low this probably corresponds to dying cells where the entire genome allows random transposition
- fragment size distribution -> since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals. We expect to see a periodic distribution of fragmetn size corresponding to nucleosomes (mono, di, tri, …), because Tn5 cannot cut DNA that is tightly wrapped around a nucleosome.
Inferring Doublets
Should be removed, because they can interfere with downstream analysis.
Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)
We can also identify doublets in the scRNA-seq space if we have paired data and remove the cells in this way.
# for each sample provided doublet information will be assigned to each cell
# this way we can remove doublet-based clusters downstream
doubScores <- addDoubletScores(
useMatrix = "TileMatrix",
input = ArrowFiles,
k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
nTrial = 5, # number of time to simulate nCell doublets
knnMethod = "UMAP", #Refers to the dimensionality reduciton method to use for nearest neighbor search.
LSIMethod = 1 # oder of normalization: tf-log(idf)
)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
# the doublet information is saved in a simpleListobject
# read in the object
doublet_summary <- readRDS("QualityControl/scATAC_BMMC_R1/scATAC_BMMC_R1-Doublet-Summary.rds")
doublet_summary[[2]] %>% head() %>% kable()
| doublet_14733 |
5.8904443 |
-3.702020 |
0.0006007 |
simulated_doublet |
0.0006007 |
| doublet_14467 |
5.5716429 |
-3.579292 |
0.0006255 |
simulated_doublet |
0.0006255 |
| doublet_4584 |
5.4819946 |
-3.693586 |
0.0006264 |
simulated_doublet |
0.0006264 |
| doublet_14409 |
0.6149653 |
3.274105 |
0.0009510 |
simulated_doublet |
0.0009510 |
| doublet_12622 |
6.3805246 |
-3.755051 |
0.0010196 |
simulated_doublet |
0.0010196 |
| doublet_5702 |
5.2130489 |
-3.519199 |
0.0010345 |
simulated_doublet |
0.0010345 |
# get first entry of the list = originalDataUMAP
p1 <- doublet_summary [[1]] %>%
ggplot() +
geom_point(aes(x = X1, y = X2, col = enrichment), size = .1) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="DoubletEnrichment")) +
labs(title = "Simulated Doublet Enrichment over expectation")
p2 <- doublet_summary [[1]] %>%
ggplot() +
geom_point(aes(x = X1, y = X2, col = score), size = .1) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="DoubletScores -log10(P-adj)")) +
labs(title = "Doublet Scores -log10(P-adj)")
p3 <- doublet_summary[[2]] %>%
ggplot() +
geom_point(aes(x = x, y = y, col = density), size = .1) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="Simulated Doublet Density")) +
labs(title = "Doublet density")
p4 <- doublet_summary[[2]] %>%
ggplot() +
geom_point(aes(x = x, y = y, col = density), size = .1) +
geom_point(data = doublet_summary[[1]], aes(x = X1, y = X2), size = .1, alpha = .4) +
scale_color_viridis_c() +
guides(fill=guide_legend(title="Simulated Doublet Density")) +
labs(title = "Simulated doublet density overlayed")
gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)

Create ArchRProject
An ArchR Project is initialized with some important attributes:
- ouput directory
- sample names
sampleColData -> matrix containint data for each sample
cellColData -> contains data associated with each cell
- after using
addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
- total number of cells (excluding doublets)
- median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
ArrowFiles = ArrowFiles,
outputDirectory = "ArchRVignette",
copyArrows = TRUE, #This is recommened so that you maintain an unaltered copy for later usage.
geneAnnotation = getGeneAnnotation(),
#genomeAnnotation = getGeneAnnotation(),
showLogo = FALSE
)
Plot QC metrics
Data before QC and corresponding plots are saved in the Quality Control output folder.
log10(unique fragments) vs TSS enrichment
- TSS enrichment score = signal-to-background
- number of unique fragments -> cells with very few fragments do not have enough data to confidently analyze them
- in the plot areas with more points/cells are colored in orange, and areas with less points in blue, indicating the distribution of cell
#create 3 separate dataframes for all samples
three_samples <- map(unique(proj$Sample), function(name){
index <- BiocGenerics::which(proj$Sample %in% name)
cells <- proj$cellNames[index]
sample_subset <- proj[cells]
df <- getCellColData(sample_subset, select = c("log10(nFrags)", "TSSEnrichment"))
p <- ggPoint(
x = df[, 1], y = df[, 2],
colorDensity = TRUE, # should the density of points on the plot be indicated by color?
continuousSet = "sambaNight",
xlabel = "Log10 unique fragments",
ylabel = "TSS enrichment",
title = paste0("Sample: ", name),
xlim = c(log10(500), quantile(df[,1], probs = 0.99)),
ylim = c(0, quantile(df[,2], probs = 0.99))
) + geom_hline(yintercept = 4, lty = "dashed") +
geom_vline(xintercept = 3, lty = "dashed")
list(plot = p, name = name)
})
gridExtra::grid.arrange(three_samples[[1]]$plot, three_samples[[2]]$plot,
three_samples[[3]]$plot, ncol = 3)

We want a TSS enrichment score of > 4 and a number of unique fragments > 1000 (log10(1000) = 3).
Plotting sample statistics
- when we have distinct samples, it can be important to compare various metric between samples
- ridge plots & violin plots are used for grouped data
p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_density(aes(x = TSSEnrichment, fill = Sample), alpha = 0.8)
## Warning: `as_data_frame()` was deprecated in tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = TSSEnrichment, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_")) %>%
ggplot() +
geom_violin(aes(x = Sample, y = TSSEnrichment, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = TSSEnrichment,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
labs(title = "TSS Enrichment")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

p1 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_density(aes(x = log10_nFrags, fill = Sample), alpha = 0.8)
p2 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
ggridges::geom_density_ridges(aes(x = log10_nFrags, y = Sample,
fill = Sample), alpha = 0.8) +
theme(legend.position = "none")
p3 <- as_data_frame(getCellColData(proj)) %>%
mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>%
ggplot() +
geom_violin(aes(x = Sample, y = log10_nFrags, fill = Sample), alpha = 0.8) +
geom_boxplot(aes(x = Sample, y = log10_nFrags,fill = Sample), alpha = 0.4) +
theme(legend.position = "none") +
labs(title = "number of fragments")
cowplot::plot_grid(p3, p2, p1, ncol = 3)

Plot Fragment Size Distribution & TSS Enrichment Profiles
- the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
- the dip is the fragment size of a nucleosome ~147bp
- TSS enrichment profiles
- clear peak in the center
- smaller shoulder peak right of the center caused by well positioned +1 nucleosome
p1 <- plotFragmentSizes(ArchRProj = proj)
p2 <- plotTSSEnrichment(ArchRProj = proj)
ggAlignPlots(p1, p2, type = "v")

Filtering Doublets
With the function addDoubleScores() information on predicted doublets has been added. Filter the putative doublets. They are not removed physically, but excluded from downstream analysis. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage which is very handy.
arguments:
- cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
- cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
- filterRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells
maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05
This way samples with different percentage of doublets will be filtered accordingly.
# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)
Dimensionality reduction & Clustering
- two other algorithms:
- latent semantic indexing (LSI) in Signac
- landmark diffusion maps (LDM) in SnapATAC
- ArchR: optimized iterative LSI method
- exhibits less susceptibility to batch effects
- focuses on most variable features
- create a LSI Reduction from a subset of the total cells
- linearly project the remaining cells into this subspace with LSI projection (based on SVD)
Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!
A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.
Iterative LSI
- compute term frequency (depth normalization to 10,000 per single cell)
\(TF = \frac{C_{ij}}{F_{j}}\) with \(C_{ij}\) being the total number of counts for peak i in cell j and \(F_{j}\) being the total number of counts in cell j.
- Inverse document frequency
- weights features by how often they occur
- more weight to less frequent peaks
\(IDF = \frac{N}{n_{p}}\) with N being the total number of cells in the dataset and \(n_{p}\) being the total number of coutns for peak i across all cells.
- The term frequency TF is normalized by the inverse document frequncy IDF. You get a TF-IDF matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.
\(TF-IDF = \log{1 + (TF * IDF) 10^{4}}\)
- SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
- Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
- Sum accessibility across all single cells in each cluster -> log-normalize
- Identify most variable features across the clusters
- repeat with most variable peaks as features
With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data
Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.
Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower initial clustering resolution. Also, the number of variable features can be lowered. #
proj <- addIterativeLSI(ArchRProj = proj, useMatrix = "TileMatrix", name = "IterativeLSI")
Clustering
Calling clusters in this new space uses the Seurat’s graph clustering function as default clustering method. The Seurat method first computs KNN graph and then a modularity optimization technique to cluster the cells (iteratively group cells together with Louvian algorithm using 10 random starts). Another option is to use “Scran”. The default number of nearest neighbors used is 10. The minimum number of cells for a cluster to be called a cluster is set to 5 by default. The maximum number of clusters to be called is set to 25 by default.
proj <- addClusters(input = proj, reducedDims = "IterativeLSI")
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
##
## Number of nodes: 10250
## Number of edges: 473760
##
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8640
## Number of communities: 13
## Elapsed time: 1 seconds
Visualizing a UMAP embedding
- uses uwot package
- various attributes of the data can be visualized
- these are stored in a matrix called
cellColData
- which variable the plot is colored by is specified by
colorBy and name parameter
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI")
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid gid value replaced by that for user 'nobody''
## Warning: invalid gid value replaced by that for user 'nobody'
df <- as_data_frame(cbind(getCellColData(proj), getEmbedding(proj)) ) %>%
rename(c(umap1 = IterativeLSI.UMAP_Dimension_1, umap2 = IterativeLSI.UMAP_Dimension_2))
variables <- c("Clusters", "Sample", "nFrags", "DoubletScore")
plots1 <- map(c("Clusters", "Sample"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .04) +
guides(col=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
plots2 <- map(c("nFrags", "DoubletScore"), function(n){
ggplot() +
geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"),
col = df %>% pull(n)), size = .04) +
scale_color_viridis_c() +
guides(fill=guide_legend(title=paste0(n))) +
xlab("umap1") +
ylab("umpa2") +
labs(title = paste0(n))
})
do.call(gridExtra::grid.arrange, c(plots1, ncol=2))#, nrow = 2))

Cluster assignment using gene scores
For the toy dataset marker genes of known hematopoietic regulators can be used. Using MAGIC we add imputation weights to smooth the dropout noise in the gene scores
proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes <- c(
"CD34", #Early Progenitor
"GATA1", #Erythroid
"PAX5", "MS4A1", "MME", #B-Cell Trajectory
"CD14", "MPO", #Monocytes
"CD3D", "CD8A"#TCells
)
p <- plotEmbedding(
ArchRProj = proj,
colorBy = "GeneScoreMatrix",
name = markerGenes,
embedding = "UMAP",
imputeWeights = getImputeWeights(proj)
)
do.call(gridExtra::grid.arrange, c(p, ncol = 3))

Visualizing Genome Browser Tracks
Browse local chromatin accessibility at marker genes. Plot genome browser tracks per cluster
p <- plotBrowserTrack(
ArchRProj = proj,
groupBy = "Clusters",
geneSymbol = markerGenes,
upstream = 50000,
downstream = 50000
)
## GRanges object with 9 ranges and 2 metadata columns:
## seqnames ranges strand | gene_id symbol
## <Rle> <IRanges> <Rle> | <character> <character>
## [1] chr1 208059883-208084683 - | 947 CD34
## [2] chrX 48644982-48652717 + | 2623 GATA1
## [3] chr9 36838531-37034476 - | 5079 PAX5
## [4] chr11 60223282-60238225 + | 931 MS4A1
## [5] chr3 154741913-154901518 + | 4311 MME
## [6] chr5 140011313-140013286 - | 929 CD14
## [7] chr17 56347217-56358296 - | 4353 MPO
## [8] chr11 118209789-118213459 - | 915 CD3D
## [9] chr2 87011728-87035519 - | 925 CD8A
## -------
## seqinfo: 24 sequences from hg19 genome
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
grid::grid.newpage()
grid::grid.draw(p$CD14)

Integration with scRNA-seq
- the scATAC-seq gene score matrix is compared with the scRNA-seq gene expression matrix
- for this alignment the
FindTransferAnchors() function from Seurat is used
- to scale for large sample size, this process is parallelized
- for each cell in ATAC we find the cell in scRNA-seq that looks most similar -> assign the correpsonding gene expression to that cell.
Apart from using this information for identifying clusters we can also use it for identifying predicted cis-regulatory elements.
if(!file.exists("scRNA-Hematopoiesis-Granja-2019.rds")){
download.file(
url = "https://jeffgranja.s3.amazonaws.com/ArchR/TestData/scRNA-Hematopoiesis-Granja-2019.rds",
destfile = "scRNA-Hematopoiesis-Granja-2019.rds"
)
}
# ranged summarized Experiment
seRNA <- readRDS("scRNA-Hematopoiesis-Granja-2019.rds")
seRNA
## class: RangedSummarizedExperiment
## dim: 20287 35582
## metadata(0):
## assays(1): counts
## rownames(20287): FAM138A OR4F5 ... S100B PRMT2
## rowData names(3): gene_name gene_id exonLength
## colnames(35582): CD34_32_R5:AAACCTGAGTATCGAA-1
## CD34_32_R5:AAACCTGAGTCGTTTG-1 ...
## BMMC_10x_GREENLEAF_REP2:TTTGTTGCATGTGTCA-1
## BMMC_10x_GREENLEAF_REP2:TTTGTTGCATTGAAAG-1
## colData names(10): Group nUMI_pre ... BioClassification Barcode
Lets have a look at the count matrix:
# sparse count matrix of scRNA-seq
assays(seRNA)[[1]][1:10, 1:5]
## 10 x 5 sparse Matrix of class "dgCMatrix"
## CD34_32_R5:AAACCTGAGTATCGAA-1 CD34_32_R5:AAACCTGAGTCGTTTG-1
## FAM138A . .
## OR4F5 . .
## AL627309.1 . .
## OR4F29 . .
## OR4F16 . .
## FAM87B . .
## LINC00115 . .
## FAM41C 1 .
## AL645608.2 . .
## SAMD11 . .
## CD34_32_R5:AAACCTGGTTCCACAA-1 CD34_32_R5:AAACGGGAGCTTCGCG-1
## FAM138A . .
## OR4F5 . .
## AL627309.1 . .
## OR4F29 . .
## OR4F16 . .
## FAM87B . .
## LINC00115 . .
## FAM41C 1 .
## AL645608.2 . .
## SAMD11 . .
## CD34_32_R5:AAACGGGAGGGAGTAA-1
## FAM138A .
## OR4F5 .
## AL627309.1 .
## OR4F29 .
## OR4F16 .
## FAM87B .
## LINC00115 .
## FAM41C .
## AL645608.2 .
## SAMD11 .
Metadata of the scRNA-seq dataset:
We already have clustering, umap embeddings and cell types.
colData(seRNA) %>% head %>% knitr::kable()
| CD34_32_R5:AAACCTGAGTATCGAA-1 |
CD34_D2T1 |
17876 |
8303 |
3187 |
Cluster1 |
-6.113410 |
4.616498 |
Cluster5 |
05_CMP.LMPP |
AAACCTGAGTATCGAA-1 |
| CD34_32_R5:AAACCTGAGTCGTTTG-1 |
CD34_D2T1 |
9277 |
3917 |
1787 |
Cluster2 |
-8.800932 |
-1.228907 |
Cluster8 |
08_GMP.Neut |
AAACCTGAGTCGTTTG-1 |
| CD34_32_R5:AAACCTGGTTCCACAA-1 |
CD34_D2T1 |
13073 |
6023 |
2552 |
Cluster3 |
-9.723482 |
7.335178 |
Cluster1 |
01_HSC |
AAACCTGGTTCCACAA-1 |
| CD34_32_R5:AAACGGGAGCTTCGCG-1 |
CD34_D2T1 |
8412 |
4493 |
2191 |
Cluster4 |
-4.293071 |
5.692705 |
Cluster6 |
06_CLP.1 |
AAACGGGAGCTTCGCG-1 |
| CD34_32_R5:AAACGGGAGGGAGTAA-1 |
CD34_D2T1 |
11914 |
5190 |
2322 |
Cluster3 |
-7.989706 |
9.108693 |
Cluster1 |
01_HSC |
AAACGGGAGGGAGTAA-1 |
| CD34_32_R5:AAACGGGAGTTACGGG-1 |
CD34_D2T1 |
12075 |
4634 |
2152 |
Cluster3 |
-7.271959 |
6.980415 |
Cluster1 |
01_HSC |
AAACGGGAGTTACGGG-1 |
Plot Quality Metrics of the scRNA-seq dataset:
as_data_frame(colData(seRNA)) %>%
select(nUMI, nGene, Group) %>%
pivot_longer(cols = !Group, names_to = "stat") %>%
ggplot() +
geom_violin(aes(x = Group, y = value, fill = Group)) +
facet_wrap(~stat, scales = "free") +
theme(axis.text.x = element_text(angle = 45, hjust = 1),
legend.position = "none") +
xlab("Sample")

#pivot_longer(cols = !s)
df <- as_data_frame(colData(seRNA))
p1 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"),
col = df %>% pull("BioClassification")), size = .04) +
guides(col=guide_legend(title="CellType")) +
xlab("umap1") +
ylab("umpa2") +
labs(title = "scRNA-seq dataset - cell type")
p2 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"),
col = df %>% pull("nGene")), size = .04) +
guides(col=guide_legend(title="number of genes")) +
scale_color_viridis_c() +
xlab("umap1") +
ylab("umpa2") +
labs(title = "scRNA-seq dataset - gene number")
gridExtra::grid.arrange(p1, p2, ncol = 1)

LS0tCnRpdGxlOiAiQXJjaFJfcGlwZWxpbmUiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGUgPSBGQUxTRSwgYXV0b2RlcCA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSBUUlVFLCBtZXNzYWdlID0gRkFMU0UpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvQXJjaFIiKQpzZXR3ZCgiL29taWNzL2dyb3Vwcy9PRTA1MzMvaW50ZXJuYWwva2F0aGFyaW5hL3NjRG9SSS9BcmNoUi8iKQoKc2V0LnNlZWQoMSkKYGBgCgoKCkFyY2hSIHRha2VzIGFzIGlucHV0IGFsaWduZWQgQkFNIG9yIGZyYWdtZW50IGZpbGVzLiBUaGVzZSBmaWxlcyBhcmUgc3RvcmVkIGluIHRoZSAKSERGNSBmaWxlIGZvcm1hdCAoaGllcmFyY2hpY2FsIGRhdGEgZm9ybWF0IHZlcnNpb241KS4gVGhlIEhERjUgZmlsZXMgYXJlIHRoZSAKY29uc3RpdHVlbnQgcGllY2VzIG9mIGFuIEFyY2hSIGFuYWx5c2lzLiBUaGV5IGFyZSBjYWxsZWQgQXJyb3cgZmlsZXMuIEFsbCBBcnJvdwpmaWxlcyBhcmUgZ3JvdXBlZCBpbnRvIGEgcHJvamVjdCwgYSBjb21wcmVzc2VkIFIgZGF0YSBmaWxlLiBUaGUgRmlsZXMgYXJlIGFjY2Vzc2VkIAppbiBtaW5pbWFsIGNodW5rcyAocGFyYWxsZWwgcmVhZCBhbmQgd3JpdGUgb3BlcmF0aW9ucykuIFRoZXJlZm9yZSwgaW4gbWVtb3J5IHdlCmRvIG5vdCBoYXZlIGFueSBsYXJnZSBmaWxlIHNpemVzLgoKVG8gc2VsZWN0IGhpZ2ggcXVhbGl0eSBjZWxscyBUU1MgZW5yaWNobWVudCBzY29yZXMgYXJlIHVzZWQuIAoKCiAgCiMgRmlyc3Qgc3RlcHMgaW4gQXJjaFIKICAKIyMgTG9hZCBsaWJyYXJpZXMKICAKYGBge3J9CmxpYnJhcnkoQXJjaFIpCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkocmhkZjUpCmxpYnJhcnkodXdvdCkKbGlicmFyeSh0aWR5dmVyc2UpCiNsaWJyYXJ5KGNhcmV0KQpoNWRpc2FibGVGaWxlTG9ja2luZygpCmBgYAogIAogIApgYGB7cn0KaW5wdXRGaWxlcyA8LSBnZXRUdXRvcmlhbERhdGEoIkhlbWF0b3BvaWVzaXMiKQpgYGAKCgpgYGB7cn0KYWRkQXJjaFJHZW5vbWUoImhnMTkiKQpgYGAKCiMjIENyZWF0ZSBBcnJvdyBGaWxlcwoKMS4gcmVhZCBhY2Nlc3NpYmxlIGZyYWdtZW50cyAKMi4gY2FsY3VsYXRlIFFDIGluZm9ybWF0aW9uIGZvciBlYWNoIGNlbGwgKFRTUyBlbnJpY2htZW50IHNjb3JlcyBhbmQgbnVjZWxvc29tZSBpbmZvKQozLiBmaWx0ZXIgY2VsbHMgYmFzZWQgb24gUUMgcGFyYW1ldGVycwo0LiBjcmVhdGUgZ2Vub21lLXdpZGUgdGlsZSBtYXRyaXggdXNpbmcgNTAwLWJwIGJpbnMKNS4gY3JlYXRlIEdlbmVTY29yZU1hdHJpeCB1c2luZyBnZW5lIGFubm90YXRpb24KCgpgYGB7cn0KQXJyb3dGaWxlcyA8LSBjcmVhdGVBcnJvd0ZpbGVzKAogIGlucHV0RmlsZXMgPSBpbnB1dEZpbGVzLAogIHNhbXBsZU5hbWVzID0gbmFtZXMoaW5wdXRGaWxlcyksCiAgbWluVFNTID0gNCwgI0RvbnQgc2V0IHRoaXMgdG9vIGhpZ2ggYmVjYXVzZSB5b3UgY2FuIGFsd2F5cyBpbmNyZWFzZSBsYXRlcgogIG1pbkZyYWdzID0gMTAwMCwgIyBtaW5pbXVtIG51bWJlciBvZiBtYXBwZWQgZnJhZ21lbnRzIHJlcXVpcmVkCiAgIyBjb3VudCBtYXRyaXgsIGluc3RlYWQgb2YgdXNpbmcgcGVhayBpdCB1c2VzIGZpeGVkLXdpZHRoIHNsaWRpbmcgd2luZG93IG9mIGJpbnMgYWNyb3NzIHRoZSB3aG9sZSBnZW5vbWUKICBhZGRUaWxlTWF0ID0gVFJVRSwgCiAgYWRkR2VuZVNjb3JlTWF0ID0gVFJVRSwgIyB1c2VzIHNpZ25hbCBwcm94aW1hbCB0byB0aGUgVFNTIHRvIGVzdGltYXRlIGdlbmUgYWN0aXZpdHkKICBzdWJUaHJlYWRpbmcgPSBGQUxTRSwKICBtYXhGcmFncyA9IDFlKzA1LAogIG1pbkZyYWdTaXplID0gMTAsCiAgbWF4RnJhZ1NpemUgPSAyMDAwLAogIFFDRGlyID0gIlF1YWxpdHlDb250cm9sIiwKICAjIHRoZSBsZW5ndGggaW4gYnAgdGhhdCB3cmFwcyBhcm91bmQgbnVjbGVvc29tZXMgLT4gCiAgICAjaWRlbnRpZnkgZnJhZ21lbnRzIGFzIHN1Yi1udWNsZW9zb21lIHNwYW5uaW5nLCBtb25vLW51Y2xlb3NvbWUgc3Bhbm5pbmcgb3IgbXVsdGktbnVjbGVvc29tZSBzcGFubmluZwogIG51Y0xlbmd0aCA9IDE0NywgCiAgIyBpbnRlZ2VyIHZlY3RvciAtPiBkZWZpbmUgcmVnaW9uIHVwL2Rvd25zdHJlYW0gb2YgVFNTIHRvIGluY2x1ZGUgYXMgcHJvbW90ZXIgcmVnaW9uCiAgIyBjYW4gYmUgdXNlZCB0byBjYWxjdWxhdGUgZS5nIGZyYWN0aW9uIG9mIHJlYWRzIGluIHByb21vdGVyIChGSVApCiAgcHJvbW90ZXJSZWdpb24gPSBjKDIwMDAsIDEwMCksCiAgIyBwYXJhbWV0ZXJzIGZvciBjb21wdXRpbmcgVFNTIGVucmljaG1lbnQgc2NvcmVzLCB3aW5kb3cgKGJwKSBjZW50ZXJlZCBhdCBUU1MgPSAxMDEKICAjIGZsYW5raW5nIHdpbmRvdyA9IDIwMDAgYnAKICAjIG5vcm0gPSBzaXplIG9mIGZsYW5rIHdpbmRvdyB1c2VkIGZvciBub3JtYWxpemF0aW9uID0gMTAwIGJwCiAgIyBhY2Nlc3NpYmlsaXR5IHdpdGhpbiAxMDEgYnAgc3Vycm91bmRpbmcgdGhlIFRTUyB3aWxsIGJlIG5vcm1hbGl6ZWQgdG8gYWNjZXNzaWJpbGl0eQogICMgaW4gMTAwIGJwIGJpbnMgZnJvbSAtMjAwMDotMTkwMWJwIGFuZCAxOTAxOiAyMDAwCiAgVFNTUGFyYW1zID0gbGlzdCgxMDEsIDIwMDAsIDEwMCksCiAgIyB3aGljaCBjaHJvbW9zb21lcyB0byBleGNsdWRlIGZvcm0gZG93bnN0cmVhbSBhbmFseXNpcwogICMgaW4gaHVtYW4gYW5kIG1vdXNlOiBtaXRvY2hvbmRyaWFsIEROQSAoY2hyTSkgYW5kIG1hbGUgc2V4IGNocm9tb3NvbWUgKGNoclkpCiAgIyB0aGUgZnJhZ21lbnRzIGFyZSBzdGlsbCBzdG9yZWQgaW4gdGhlIGFycm93IGZpbGVzCiAgZXhjbHVkZUNociA9IGMoImNock0iLCAiY2hyWSIpLAogICMgbnVtYmVyIG9mIGNodW5rcyB0byBkaXZpZGUgY2hyb21vc29tZXMgaW4gLT4gbG93LW1lbW9yeSBwYXJhbGxlbGl6ZWQgcmVhZGluZyBvZiBpbnB1dCBmaWxlcwogIG5DaHVuayA9IDUsCiAgIyBuYW1lIG9mIGZpZWxkIGluIGlucHV0IGJhbSBmaWxlIGNvbnRhaW5pbmcgdGhlIGJhcmNvZGUgdGFnIGluZm9ybWF0aW9uCiAgYmNUYWcgPSAicW5hbWUiLAogIG9mZnNldFBsdXMgPSA0LCAjIG9mZnNldCBhcHBsaWVkIHRvICsgc3RyYW5kZWQgVG41IGluc2VydGlvbiAtPiBhY2NvdW50IGZvciBwcmVjaXNlIFRuNSBiaW5kaW5nIHNpdGUKICBvZmZzZXRNaW51cyA9IC01LCAKICBsb2dGaWxlID0gY3JlYXRlTG9nRmlsZSgiY3JlYXRlQXJyb3dzIikKKQpgYGAKCiMjIFF1YWxpdHkgQ29udHJvbAoKSGF2ZSBhIGxvb2sgYXQgdGhpcyBmb3IgYWRkaXRpb25hbCBRQyEKaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0FUQUNzZXFRQy9pbnN0L2RvYy9BVEFDc2VxUUMuaHRtbAoKMS4gdGhlIG51bWJlciBvZiAqKnVuaXF1ZSBudWNsZWFyIGZyYWdtZW50cyoqIChhcyBvcHBvc2VkIHRvIG1pdG9jaG9uZHJpYWwgZnJhZ21lbnRzKQpBIGNlbGwgd2l0aCB2ZXJ5IGZldyB1c2FibGUgZnJhZ21lbnRzIHdpbGwgbm90IHByb3ZpZGUgZW5vdWdoIGRhdGEgdG8gbWFrIHVzZWZ1bCBjb25jbHVzaW9ucy4KMi4gKipzaWduYWwtdG8tYmFja2dyb3VuZCByYXRpbyoqIC0+IGlmIHRoaXMgaXMgbG93IHRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gZHlpbmcKY2VsbHMgd2hlcmUgdGhlIGVudGlyZSBnZW5vbWUgYWxsb3dzIHJhbmRvbSB0cmFuc3Bvc2l0aW9uCjMuICoqZnJhZ21lbnQgc2l6ZSBkaXN0cmlidXRpb24qKiAtPiBzaW5jZSAxNDcgYnAgYXJlIHdyYXBwZWQgYXJvdW5kIGEgbnVjbGVvc29tZSBpdCBpcyAKZXhwZWN0ZWQgdGhhdCB0aGVyZSBhcmUgZGVwbGV0aW9ucyBvZiBmcmFnbWVudHMgb2YgdGhpcyBsZW5ndGggYXQgcmVndWxhciBpbnRlcnZhbHMuIApXZSBleHBlY3QgdG8gc2VlIGEgcGVyaW9kaWMgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZXRuIHNpemUgY29ycmVzcG9uZGluZyB0byBudWNsZW9zb21lcwoobW9ubywgZGksIHRyaSwgLi4uKSwgYmVjYXVzZSBUbjUgY2Fubm90IGN1dCBETkEgdGhhdCBpcyB0aWdodGx5IHdyYXBwZWQgYXJvdW5kIAphIG51Y2xlb3NvbWUuIAoKCiMjIEluZmVycmluZyBEb3VibGV0cwoKU2hvdWxkIGJlIHJlbW92ZWQsIGJlY2F1c2UgdGhleSBjYW4gaW50ZXJmZXJlIHdpdGggZG93bnN0cmVhbSBhbmFseXNpcy4KCioqRG91YmxldCBkZXRlY3Rpb24tYW5kLXJlbW92YWwgYWxnb3JpdGhtOioqCkhldGVyb3R5cGljIGRvdWJsZXRzIGFyZSBpZGVudGlmaWVkIGJ5IGdlbmVyYXRpbmcgYSBjb2xsZWN0aW9uIG9mIHN5bnRoZXRpYyBkb3VibGV0cy4KVGhlc2Ugc3ludGhldGljIGRvdWJsZXRzIGFyZSBwcm9qZWN0ZWQgaW50byBsb3ctZGltZW5zaW9uYWwgZW1iZWRkaW5ncy4gU2VhcmNoaW5nCmZvciBuZWFyZXN0IG5laWdoYm91cnMgdG8gdGhlIHN5bnRoZXRpYyBkb3VibGV0cyB3ZSBjYW4gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlCmRhdGFzZXQuIFRoaXMgb3V0cGVyZm9ybXMgdGhlIHByZWRpY3Rpb24gb2YgZG91YmxldHMgdXNpbmcgZnJhZ21lbnQgbnVtYmVyCihST0MtQVVDKS4gKENvbXBhcmVkIHRvIGRlbXV4bGV0IGFzIGdyb3VuZCB0cnV0aCkKCioqV2UgY2FuIGFsc28gaWRlbnRpZnkgZG91YmxldHMgaW4gdGhlIHNjUk5BLXNlcSBzcGFjZSBpZiB3ZSBoYXZlIHBhaXJlZCBkYXRhCmFuZCByZW1vdmUgdGhlIGNlbGxzIGluIHRoaXMgd2F5LioqCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodD01LCByZXN1bHRzPSAiYXNpcyJ9CiMgZm9yIGVhY2ggc2FtcGxlIHByb3ZpZGVkIGRvdWJsZXQgaW5mb3JtYXRpb24gd2lsbCBiZSBhc3NpZ25lZCB0byBlYWNoIGNlbGwKIyB0aGlzIHdheSB3ZSBjYW4gcmVtb3ZlIGRvdWJsZXQtYmFzZWQgY2x1c3RlcnMgZG93bnN0cmVhbQpkb3ViU2NvcmVzIDwtIGFkZERvdWJsZXRTY29yZXMoCiAgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLAogIGlucHV0ID0gQXJyb3dGaWxlcywKICBrID0gMTAsICNSZWZlcnMgdG8gaG93IG1hbnkgY2VsbHMgbmVhciBhICJwc2V1ZG8tZG91YmxldCIgdG8gY291bnQuCiAgblRyaWFsID0gNSwgIyBudW1iZXIgb2YgdGltZSB0byBzaW11bGF0ZSBuQ2VsbCBkb3VibGV0cyAKICBrbm5NZXRob2QgPSAiVU1BUCIsICNSZWZlcnMgdG8gdGhlIGRpbWVuc2lvbmFsaXR5IHJlZHVjaXRvbiBtZXRob2QgdG8gdXNlIGZvciBuZWFyZXN0IG5laWdoYm9yIHNlYXJjaC4KICBMU0lNZXRob2QgPSAxICMgb2RlciBvZiBub3JtYWxpemF0aW9uOiB0Zi1sb2coaWRmKQopCgojIHRoZSBkb3VibGV0IGluZm9ybWF0aW9uIGlzIHNhdmVkIGluIGEgc2ltcGxlTGlzdG9iamVjdAojIHJlYWQgaW4gdGhlIG9iamVjdApkb3VibGV0X3N1bW1hcnkgPC0gcmVhZFJEUygiUXVhbGl0eUNvbnRyb2wvc2NBVEFDX0JNTUNfUjEvc2NBVEFDX0JNTUNfUjEtRG91YmxldC1TdW1tYXJ5LnJkcyIpCgpkb3VibGV0X3N1bW1hcnlbWzJdXSAlPiUgaGVhZCgpICU+JSBrYWJsZSgpCgojIGdldCBmaXJzdCBlbnRyeSBvZiB0aGUgbGlzdCA9IG9yaWdpbmFsRGF0YVVNQVAKcDEgPC0gZG91YmxldF9zdW1tYXJ5IFtbMV1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbCA9IGVucmljaG1lbnQpLCBzaXplID0gLjEpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJEb3VibGV0RW5yaWNobWVudCIpKSArCiAgbGFicyh0aXRsZSA9ICJTaW11bGF0ZWQgRG91YmxldCBFbnJpY2htZW50IG92ZXIgZXhwZWN0YXRpb24iKQoKcDIgPC0gZG91YmxldF9zdW1tYXJ5IFtbMV1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IFgxLCB5ID0gWDIsIGNvbCA9IHNjb3JlKSwgc2l6ZSA9IC4xKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iRG91YmxldFNjb3JlcyAtbG9nMTAoUC1hZGopIikpICsKICBsYWJzKHRpdGxlID0gIkRvdWJsZXQgU2NvcmVzIC1sb2cxMChQLWFkaikiKQoKCnAzIDwtIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iU2ltdWxhdGVkIERvdWJsZXQgRGVuc2l0eSIpKSArCiAgbGFicyh0aXRsZSA9ICJEb3VibGV0IGRlbnNpdHkiKQoKCnA0IDwtIGRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHgsIHkgPSB5LCBjb2wgPSBkZW5zaXR5KSwgc2l6ZSA9IC4xKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZG91YmxldF9zdW1tYXJ5W1sxXV0sIGFlcyh4ID0gWDEsIHkgPSBYMiksIHNpemUgPSAuMSwgYWxwaGEgPSAuNCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9IlNpbXVsYXRlZCBEb3VibGV0IERlbnNpdHkiKSkgKwogIGxhYnModGl0bGUgPSAiU2ltdWxhdGVkIGRvdWJsZXQgZGVuc2l0eSBvdmVybGF5ZWQiKQoKZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDEsIHAyLCBwMywgcDQsIG5jb2wgPSAyKQoKYGBgCgoKIyMgQ3JlYXRlIEFyY2hSUHJvamVjdAoKQW4gQXJjaFIgUHJvamVjdCBpcyBpbml0aWFsaXplZCB3aXRoIHNvbWUgaW1wb3J0YW50IGF0dHJpYnV0ZXM6CgoqIG91cHV0IGRpcmVjdG9yeQoqIHNhbXBsZSBuYW1lcwoqIGBzYW1wbGVDb2xEYXRhYCAtPiBtYXRyaXggY29udGFpbmludCBkYXRhIGZvciBlYWNoIHNhbXBsZQoqIGBjZWxsQ29sRGF0YWAgLT4gY29udGFpbnMgZGF0YSBhc3NvY2lhdGVkIHdpdGggZWFjaCBjZWxsCiAgKyBhZnRlciB1c2luZyBgYWRkRG91YmxldFNjb3JlKClgIHRoZXJlIHdpbGwgYmUgYSBjb2x1bW4gCiAgZm9yICJEb3VibGV0IEVucmljaG1lbnQiIGFuZCAiRG91YmxldCBTY29yZSIKKiB0b3RhbCBudW1iZXIgb2YgY2VsbHMgKGV4Y2x1ZGluZyBkb3VibGV0cykKKiBtZWRpYW4gVFNTIHNjb3JlICYgbWVkaWFuIG51bWJlciBvZiBmcmFnbWVudHMgYWNyb3NzIGFsbCBjZWxscyAKYW5kIHNhbXBsZXMKCmBgYHtyfQpwcm9qIDwtIEFyY2hSUHJvamVjdCgKICBBcnJvd0ZpbGVzID0gQXJyb3dGaWxlcywgCiAgb3V0cHV0RGlyZWN0b3J5ID0gIkFyY2hSVmlnbmV0dGUiLAogIGNvcHlBcnJvd3MgPSBUUlVFLCAjVGhpcyBpcyByZWNvbW1lbmVkIHNvIHRoYXQgeW91IG1haW50YWluIGFuIHVuYWx0ZXJlZCBjb3B5IGZvciBsYXRlciB1c2FnZS4KICBnZW5lQW5ub3RhdGlvbiA9IGdldEdlbmVBbm5vdGF0aW9uKCksCiAgI2dlbm9tZUFubm90YXRpb24gPSBnZXRHZW5lQW5ub3RhdGlvbigpLAogIHNob3dMb2dvID0gRkFMU0UKKQpgYGAKCgojIFBsb3QgUUMgbWV0cmljcyAKCkRhdGEgYmVmb3JlIFFDIGFuZCBjb3JyZXNwb25kaW5nIHBsb3RzIGFyZSBzYXZlZCBpbiB0aGUgUXVhbGl0eSBDb250cm9sIG91dHB1dCBmb2xkZXIuCgojIyBsb2cxMCh1bmlxdWUgZnJhZ21lbnRzKSB2cyBUU1MgZW5yaWNobWVudAoKKiBUU1MgZW5yaWNobWVudCBzY29yZSA9IHNpZ25hbC10by1iYWNrZ3JvdW5kIAoqIG51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIC0+IGNlbGxzIHdpdGggdmVyeSBmZXcgZnJhZ21lbnRzIGRvIG5vdCBoYXZlIGVub3VnaCAKZGF0YSB0byBjb25maWRlbnRseSBhbmFseXplIHRoZW0gCiogaW4gdGhlIHBsb3QgYXJlYXMgd2l0aCBtb3JlIHBvaW50cy9jZWxscyBhcmUgY29sb3JlZCBpbiBvcmFuZ2UsIGFuZCBhcmVhcwp3aXRoIGxlc3MgcG9pbnRzIGluIGJsdWUsIGluZGljYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsCgoKYGBge3IsIGZpZy53aWR0aD04fQojY3JlYXRlIDMgc2VwYXJhdGUgZGF0YWZyYW1lcyBmb3IgYWxsIHNhbXBsZXMKdGhyZWVfc2FtcGxlcyA8LSBtYXAodW5pcXVlKHByb2okU2FtcGxlKSwgZnVuY3Rpb24obmFtZSl7CiAgaW5kZXggPC0gQmlvY0dlbmVyaWNzOjp3aGljaChwcm9qJFNhbXBsZSAlaW4lIG5hbWUpCiAgY2VsbHMgPC0gcHJvaiRjZWxsTmFtZXNbaW5kZXhdCiAgc2FtcGxlX3N1YnNldCA8LSBwcm9qW2NlbGxzXQogIGRmIDwtIGdldENlbGxDb2xEYXRhKHNhbXBsZV9zdWJzZXQsIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAiVFNTRW5yaWNobWVudCIpKQogIHAgPC0gZ2dQb2ludCgKICAgIHggPSBkZlssIDFdLCB5ID0gZGZbLCAyXSwgCiAgICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogICAgY29udGludW91c1NldCA9ICJzYW1iYU5pZ2h0IiwgCiAgICB4bGFiZWwgPSAiTG9nMTAgdW5pcXVlIGZyYWdtZW50cyIsCiAgICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogICAgdGl0bGUgPSBwYXN0ZTAoIlNhbXBsZTogIiwgbmFtZSksCiAgICB4bGltID0gYyhsb2cxMCg1MDApLCBxdWFudGlsZShkZlssMV0sIHByb2JzID0gMC45OSkpLAogICAgeWxpbSA9IGMoMCwgcXVhbnRpbGUoZGZbLDJdLCBwcm9icyA9IDAuOTkpKQogICAgKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDQsIGx0eSA9ICJkYXNoZWQiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAzLCBsdHkgPSAiZGFzaGVkIikKICBsaXN0KHBsb3QgPSBwLCBuYW1lID0gbmFtZSkKfSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHRocmVlX3NhbXBsZXNbWzFdXSRwbG90LCB0aHJlZV9zYW1wbGVzW1syXV0kcGxvdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVlX3NhbXBsZXNbWzNdXSRwbG90LCBuY29sID0gMykKCgpgYGAKCldlIHdhbnQgYSBUU1MgZW5yaWNobWVudCBzY29yZSBvZiA+IDQgYW5kIGEgbnVtYmVyIG9mIHVuaXF1ZSBmcmFnbWVudHMgPiAxMDAwIChsb2cxMCgxMDAwKSA9IDMpLiAKCgojIyMgUGxvdHRpbmcgc2FtcGxlIHN0YXRpc3RpY3MKCiogd2hlbiB3ZSBoYXZlIGRpc3RpbmN0IHNhbXBsZXMsIGl0IGNhbiBiZSBpbXBvcnRhbnQgdG8gY29tcGFyZSB2YXJpb3VzCm1ldHJpYyBiZXR3ZWVuIHNhbXBsZXMKKiByaWRnZSBwbG90cyAmIHZpb2xpbiBwbG90cyBhcmUgdXNlZCBmb3IgZ3JvdXBlZCBkYXRhCgpgYGB7cixmaWcud2lkdGg9MTB9CgpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgCgpwMiA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdncmlkZ2VzOjpnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyh4ID0gVFNTRW5yaWNobWVudCwgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC40KSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVFNTIEVucmljaG1lbnQiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKYGBge3IsZmlnLndpZHRoPTEwfQpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KQoKcDIgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZ3JpZGdlczo6Z2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IGxvZzEwX25GcmFncywgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gU2FtcGxlLCB5ID0gbG9nMTBfbkZyYWdzLGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuNCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiBmcmFnbWVudHMiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKIyMjIFBsb3QgRnJhZ21lbnQgU2l6ZSBEaXN0cmlidXRpb24gJiBUU1MgRW5yaWNobWVudCBQcm9maWxlcwoKKiB0aGUgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZW50cyBzaXplIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudCBiZXR3ZWVuIHNhbXBsZXMsCmNlbGwgdHlwZXMgYW5kIGJhdGNoZXMgLT4gdGhlc2UgZGlmZmVyZW5jZXMgZG8gbm90IG5lY2Vzc2FyaWx5IGNvcnJlbGF0ZSB3aXRoIApkaWZmZXJlbmNlcyBpbiBxdWFsaXR5CiogdGhlIGRpcCBpcyB0aGUgZnJhZ21lbnQgc2l6ZSBvZiBhIG51Y2xlb3NvbWUgfjE0N2JwCiogVFNTIGVucmljaG1lbnQgcHJvZmlsZXMKICArIGNsZWFyIHBlYWsgaW4gdGhlIGNlbnRlciAKICArIHNtYWxsZXIgc2hvdWxkZXIgcGVhayByaWdodCBvZiB0aGUgY2VudGVyIGNhdXNlZCBieSB3ZWxsIHBvc2l0aW9uZWQgKzEgbnVjbGVvc29tZQoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CnAxIDwtIHBsb3RGcmFnbWVudFNpemVzKEFyY2hSUHJvaiA9IHByb2opCnAyIDwtIHBsb3RUU1NFbnJpY2htZW50KEFyY2hSUHJvaiA9IHByb2opCmdnQWxpZ25QbG90cyhwMSwgcDIsIHR5cGUgPSAidiIpCmBgYAoKIyBGaWx0ZXJpbmcgRG91YmxldHMKCldpdGggdGhlIGZ1bmN0aW9uIGBhZGREb3VibGVTY29yZXMoKWAgaW5mb3JtYXRpb24gb24gcHJlZGljdGVkIGRvdWJsZXRzIGhhcyBiZWVuCmFkZGVkLiBGaWx0ZXIgdGhlIHB1dGF0aXZlIGRvdWJsZXRzLiBUaGV5IGFyZSBub3QgcmVtb3ZlZCBwaHlzaWNhbGx5LCBidXQgCmV4Y2x1ZGVkIGZyb20gZG93bnN0cmVhbSBhbmFseXNpcy4gQXJjaFIgYXV0b21hdGljYWxseSBwcmludHMgdGhlIG51bWJlciBvZgpjZWxscyByZW1vdmVkIGZyb20gZWFjaCBzYW1wbGUgYW5kIHRoZSBjb3JyZXNwb25kaW5nCnBlcmNlbnRhZ2Ugd2hpY2ggaXMgdmVyeSBoYW5keS4KCioqYXJndW1lbnRzOioqCgoqIGN1dEVucmljaCA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0RW5yaWNobWVudCwgbnVtYmVyIG9mIHNpbXVsYXRlZCAKZG91YmxldHMgZGl2aWRlZCBieSBleHBlY3RlZCBudW1iZXIgZ2l2ZW4gYSByYW5kb20gdW5pZm9ybSBkaXN0cmlidXRpb24KKiBjdXRTY29yZSA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0IFNjb3JlLCByZXByZXNlbnRzIC1sb2cxMChiaW5vbWlhbCBhZGp1c3RlZCBwLXZhbHVlKQpmb3IgdGhlIERvdWJsZXRFbnJpY2htZW50YWRkCiogZmlsdGVyUmF0aW8gPSBtYXhpbXVtIHJhdGlvIG9mIHByZWRpY3RlZCBkb3VibGV0cyB0byBmaWx0ZXIgYmFzZWQgb24gbnVtYmVyIG9mIApwYXNzLWZpbHRlciBjZWxscyAoQSBoaWdoZXIgZmlsdGVyUmF0aW8gbWVhbnMgdGhhdCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkKQplLmcuIDUwMDAgY2VsbHMKCm1heGltdW0gd291bGQgYmUgZmlsdGVyUmF0aW8gKiA1MDAwIC8gMTAwMDAwID0gZmlsdGVyUmF0aW8gKiA1MDAwICogMC4wNQoKKipUaGlzIHdheSBzYW1wbGVzIHdpdGggZGlmZmVyZW50IHBlcmNlbnRhZ2Ugb2YgZG91YmxldHMgd2lsbCBiZSBmaWx0ZXJlZCBhY2NvcmRpbmdseS4qKgoKYGBge3J9CiMgaW4gb3VyIGNhc2Ugd2Ugbm93IGhhdmUgMTAgMjUxIGNlbGxzIGFzIG9wcG9zZWQgdG8gMTAgNjYxIGNlbGxzIGJlZm9yZQojIGZpbHRlcmluZyAtPiA0MTAgY2VsbHMgd2VyZSByZW1vdmVkICgzLjg1JSkKcHJvaiA8LSBmaWx0ZXJEb3VibGV0cyhBcmNoUlByb2ogPSBwcm9qKQpgYGAKCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAmIENsdXN0ZXJpbmcKCiogdHdvIG90aGVyIGFsZ29yaXRobXM6CiAgKyBsYXRlbnQgc2VtYW50aWMgaW5kZXhpbmcgKExTSSkgaW4gU2lnbmFjCiAgKyBsYW5kbWFyayBkaWZmdXNpb24gbWFwcyAoTERNKSBpbiBTbmFwQVRBQwogIAoqIEFyY2hSOiBvcHRpbWl6ZWQgaXRlcmF0aXZlIExTSSBtZXRob2QgCiAgKyBleGhpYml0cyBsZXNzIHN1c2NlcHRpYmlsaXR5IHRvIGJhdGNoIGVmZmVjdHMgCiAgKyBmb2N1c2VzIG9uIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgCiAgMS4gY3JlYXRlIGEgTFNJIFJlZHVjdGlvbiBmcm9tIGEgc3Vic2V0IG9mIHRoZSB0b3RhbCBjZWxscwogIDIuIGxpbmVhcmx5IHByb2plY3QgdGhlIHJlbWFpbmluZyBjZWxscyBpbnRvIHRoaXMgc3Vic3BhY2Ugd2l0aCBMU0kgcHJvamVjdGlvbgogIChiYXNlZCBvbiBTVkQpCgpCZWNhdXNlIHdlIGNhbiBoYXZlIG1heGltYWxseSB0d28gYWNjZXNzaWJsZSBhbGxlbGVzIHBlciBjZWxsLCB0aGUgc2NBVEFDLXNlcSBkYXRhCmlzIHNwYXJzZS4gVGhlcmVmb3JlLCB0aGUgbWFqb3JpdHkgb2YgYWNjZXNzaWJsZSByZWdpb25zIGFyZSBub3QgdHJhbnNwb3NlZCwgbWVhbmluZyAKdGhhdCBtb3N0IGxvY2kgd2lsbCBoYXZlIDAgYWNjZXNzaWJsZSBhbGxlbGVzLiBBIHplcm8gY291bGQgbWVhbiAibm9uLWFjY2Vzc2libGUiIApvciAibm90IHNhbXBsZWQiLiBGb3IgbWFueSBhbmFseXNpcyB3ZSBjYW4gdXNlIGEgYmluYXJpemVkIG1hdGl4LiAqKkltcG9yYW50bHksKioKKip0aGUgMXMgaGF2ZSBpbmZvcm1hdGlvbiwgQlVUIHRoZSAwcyBkbyBub3QhKioKCkEgUENBIHdvdWxkIHJlc3VsdCBpbiBoaWdoIGludGVyLWNlbGwgc2ltaWxhcml0eSBhdCBhbGwgMCBwb3NpdGlvbnMuIEFuIGFsdGVybmF0aXZlCmFwcHJvYWNoIGZvciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gaXMgYSAqKmxheWVyZWQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uKiouIEZpcnN0LAoqKkxhdGVudCBTZW1hbnRpYyBJbmRleGluZyAoTFNJKSoqIGlzIHVzZWQuIExTSSBpcyBhbiBhcHByb2FjaCBmcm9tIGxhbmd1YWdlCnByb2Nlc3NpbmcuIERpZmZlcmVudCBzYW1wbGVzIGFyZSB0aGUgImRvY3VtZW50cyIgYW5kIGRpZmZlcmVudCByZWdpb25zL3BlYWtzIGFyZQp0aGUgIndvcmRzIi4gCgojIyBJdGVyYXRpdmUgTFNJCgoxLiBjb21wdXRlIHRlcm0gZnJlcXVlbmN5IChkZXB0aCBub3JtYWxpemF0aW9uIHRvIDEwLDAwMCBwZXIgc2luZ2xlIGNlbGwpCgokVEYgPSBcZnJhY3tDX3tpan19e0Zfe2p9fSQgd2l0aCAkQ197aWp9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBmb3IgcGVhayBpIGluIGNlbGwgaiBhbmQgJEZfe2p9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBpbiBjZWxsIGouCgoyLiBJbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSAKICAqIHdlaWdodHMgZmVhdHVyZXMgYnkgaG93IG9mdGVuIHRoZXkgb2NjdXIgCiAgKiBtb3JlIHdlaWdodCB0byBsZXNzIGZyZXF1ZW50IHBlYWtzCgokSURGID0gXGZyYWN7Tn17bl97cH19JCB3aXRoIE4gYmVpbmcgdGhlIHRvdGFsIG51bWJlciBvZiBjZWxscyBpbiB0aGUgZGF0YXNldCBhbmQgJG5fe3B9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdXRucyBmb3IgcGVhayBpIGFjcm9zcyBhbGwgY2VsbHMuCgozLiBUaGUgdGVybSBmcmVxdWVuY3kgVEYgaXMgbm9ybWFsaXplZCBieSB0aGUgaW52ZXJzZSBkb2N1bWVudCBmcmVxdW5jeSBJREYuIApZb3UgZ2V0IGEgKipURi1JREYqKiBtYXRyaXggKHRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5KSB3aGljaAp0ZWxscyB1cyBob3cgaW1wb3J0YW50IGEgcmVnaW9uL3BlYWsgaXMgdG8gYSBzYW1wbGUuIEluIG90aGVyIHdvcmRzIHlvdSB0cmFuc2Zvcm0KYSBiaW5hcnkgbWF0cml4IHRvIGEgbm9uLWJpbmFyeSBtYXRyaXguCgokVEYtSURGID0gXGxvZ3sxICsgKFRGICogSURGKSAxMF57NH19JAoKNC4gU1ZEIGlkZW50aWZpZXMgdGhlIG1vc3QgdmFsdWFibGUgaW5mb3JtYXRpb24gYWNyb3NzIHNhbXBsZXMuIFRoZW4gCndlIGNhbiB1c2UgdGhlc2UgbW9zdCB2YWx1YWJsZSBmZWF0dXJlcyB0byByZXByZXNlbnQgdGhlIGRhdGEgaW4gYSBsb3dlciBkaW1lbnNpb25hbCBzcGFjZQo1LiBDbHVzdGVycyBhcmUgaWRlbnRpZmllZCB3aXRoIFNldXJhdCdzIFNoYXJlZCBOZWFyZXN0IE5laWdoYm9yIGNsdXN0ZXJpbmcKNi4gU3VtIGFjY2Vzc2liaWxpdHkgYWNyb3NzIGFsbCBzaW5nbGUgY2VsbHMgaW4gZWFjaCBjbHVzdGVyIC0+IGxvZy1ub3JtYWxpemUKNy4gSWRlbnRpZnkgbW9zdCB2YXJpYWJsZSBmZWF0dXJlcyBhY3Jvc3MgdGhlIGNsdXN0ZXJzCjguIHJlcGVhdCB3aXRoIG1vc3QgdmFyaWFibGUgcGVha3MgYXMgZmVhdHVyZXMKCldpdGggTFNJIHdlIGNhbiByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBzcGFyc2UgaW5zZXJ0aW9uIG1hdHJpeCB0byB0ZW5zIApvciBodW5kcmVkcy4gVGhlbiBVTUFQIG9yIHQtU05FIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YQoKClVubGlrZSBpbiBzY1JOQS1zZXEgd2UgY2Fubm90IHNlbGVjdCB0aGUgdG9wIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyBiZWZvcmUgCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAoaGlnaCBub2lzZSwgbG93IHJlcHJvZHVjaWJpbGl0eSkuIFJhdGhlciB0aGUgaXRlcmF0aXZlIApMU0kgYXBwcm9hY2ggZmlyc3QgY29tcHV0ZXMgYSBMU0kgCm9uIHRoZSBtb3N0IGFjY2Vzc2libGUgdGlsZXMgKHRoaXMgd2lsbCBpZGVudGlmeSBjbHVzdGVycyBjb3JyZXNwb25kaW5nIHRvIHRoZSAKbWFqb3IgY2VsbCB0eXBlcykuIFRoZW4sIEFyY2hSIGNvbXB1dGVzIHRoZSBhdmVyYWdlIGFjY2Vzc2liaWxpdHkgYWNyb3NzIHRoZXNlIApjbHVzdGVycyBhY3Jvc3MgYWxsIGZlYXR1cmVzLiBOZXh0LCB0aGUgbW9zdCB2YXJpYWJsZSBwZWFrcyBhY3Jvc3MgdGhlc2UgY2x1c3RlcnMKYXJlIGlkZW50aWZpZWQuIFRoZSBtb3N0IGhpZ2hseSBhY2Nlc3NpYmxlIHBlYWtzIGFyZSB0aGUgZmVhdHVyZXMgb2YgYSBuZXcgCnJvdW5kIG9mIExTSS4gV2UgY2FuIHNldCBob3cgbWFueSByb3VuZHMgb2YgTFNJIHdlIHdhbnQgdG8gYmUgcGVmb3JtZWQuIAoKVXNpbmcgaXRlcmF0aXZlIExTSSByZWR1Y2VzIGJhdGNoIGVmZmVjdHMuIElmIHlvdSBzZWUgc29tZSBiYXRjaCBlZmZlY3RzIHlvdSBjb3VsZAp0cnkgdG8gYWRkIG1vcmUgTFNJIGl0ZXJhdGlvbnMgYW5kIHN0YXJ0IGZyb20gYSBsb3dlciBpbml0aWFsIGNsdXN0ZXJpbmcgCnJlc29sdXRpb24uIEFsc28sIHRoZSBudW1iZXIgb2YgdmFyaWFibGUgZmVhdHVyZXMgY2FuIGJlIGxvd2VyZWQuICAjCgoKYGBge3J9CnByb2ogPC0gYWRkSXRlcmF0aXZlTFNJKEFyY2hSUHJvaiA9IHByb2osIHVzZU1hdHJpeCA9ICJUaWxlTWF0cml4IiwgbmFtZSA9ICJJdGVyYXRpdmVMU0kiKQoKYGBgCgoKIyMgQ2x1c3RlcmluZwoKQ2FsbGluZyBjbHVzdGVycyBpbiB0aGlzIG5ldyBzcGFjZSB1c2VzIHRoZSBTZXVyYXQncyBncmFwaCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmFzIGRlZmF1bHQgY2x1c3RlcmluZyBtZXRob2QuIFRoZSBTZXVyYXQgCm1ldGhvZCBmaXJzdCBjb21wdXRzIEtOTiBncmFwaCBhbmQgdGhlbiBhICBtb2R1bGFyaXR5IG9wdGltaXphdGlvbgp0ZWNobmlxdWUgdG8gY2x1c3RlciB0aGUgY2VsbHMgKGl0ZXJhdGl2ZWx5IGdyb3VwIGNlbGxzIHRvZ2V0aGVyCndpdGggTG91dmlhbiBhbGdvcml0aG0gdXNpbmcgMTAgcmFuZG9tIHN0YXJ0cykuIEFub3RoZXIgb3B0aW9uIAppcyB0byB1c2UgIlNjcmFuIi4gVGhlCmRlZmF1bHQgbnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3JzIHVzZWQKaXMgMTAuIFRoZSBtaW5pbXVtIG51bWJlciBvZiBjZWxscyBmb3IgYSBjbHVzdGVyIHRvIGJlIGNhbGxlZCBhIApjbHVzdGVyIGlzIHNldCB0byA1IGJ5IGRlZmF1bHQuIFRoZSBtYXhpbXVtIG51bWJlciBvZiBjbHVzdGVycyB0byAKYmUgY2FsbGVkIGlzIHNldCB0byAyNSBieSBkZWZhdWx0LiAKCmBgYHtyfQpwcm9qIDwtIGFkZENsdXN0ZXJzKGlucHV0ID0gcHJvaiwgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIikKYGBgCgoKIyBWaXN1YWxpemluZyBhIFVNQVAgZW1iZWRkaW5nCgoqIHVzZXMgdXdvdCBwYWNrYWdlCiogdmFyaW91cyBhdHRyaWJ1dGVzIG9mIHRoZSBkYXRhIGNhbiBiZSB2aXN1YWxpemVkCiAgKyB0aGVzZSBhcmUgc3RvcmVkIGluIGEgbWF0cml4IGNhbGxlZCBgY2VsbENvbERhdGFgCiAgKyB3aGljaCB2YXJpYWJsZSB0aGUgcGxvdCBpcyBjb2xvcmVkIGJ5IGlzIHNwZWNpZmllZCBieSBgY29sb3JCeWAKICBhbmQgYG5hbWVgIHBhcmFtZXRlcgoKYGBge3J9CnByb2ogPC0gYWRkVU1BUChBcmNoUlByb2ogPSBwcm9qLCByZWR1Y2VkRGltcyA9ICJJdGVyYXRpdmVMU0kiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmRmIDwtIGFzX2RhdGFfZnJhbWUoY2JpbmQoZ2V0Q2VsbENvbERhdGEocHJvaiksIGdldEVtYmVkZGluZyhwcm9qKSkgKSAlPiUKICByZW5hbWUoYyh1bWFwMSA9IEl0ZXJhdGl2ZUxTSS5VTUFQX0RpbWVuc2lvbl8xLCB1bWFwMiAgPSBJdGVyYXRpdmVMU0kuVU1BUF9EaW1lbnNpb25fMikpCgp2YXJpYWJsZXMgPC0gYygiQ2x1c3RlcnMiLCAiU2FtcGxlIiwgIm5GcmFncyIsICJEb3VibGV0U2NvcmUiKQoKcGxvdHMxIDwtIG1hcChjKCJDbHVzdGVycyIsICJTYW1wbGUiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksIHNpemUgPSAuMDQpICsKICAgIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKcGxvdHMyIDwtIG1hcChjKCJuRnJhZ3MiLCAiRG91YmxldFNjb3JlIiksIGZ1bmN0aW9uKG4pewogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoInVtYXAxIiksIHkgPSBkZiAlPiUgcHVsbCgidW1hcDIiKSwgCiAgICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwobikpLCBzaXplID0gLjA0KSArCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9cGFzdGUwKG4pKSkgKwogICAgeGxhYigidW1hcDEiKSArCiAgICB5bGFiKCJ1bXBhMiIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobikpCn0pCgpkby5jYWxsKGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlLCBjKHBsb3RzMSwgbmNvbD0yKSkjLCBucm93ID0gMikpCgpgYGAKCgojIENsdXN0ZXIgYXNzaWdubWVudCB1c2luZyBnZW5lIHNjb3JlcwoKRm9yIHRoZSB0b3kgZGF0YXNldCBtYXJrZXIgZ2VuZXMgb2Yga25vd24gaGVtYXRvcG9pZXRpYyByZWd1bGF0b3JzCmNhbiBiZSB1c2VkLiBVc2luZyBNQUdJQyB3ZSBhZGQgaW1wdXRhdGlvbiB3ZWlnaHRzIHRvIHNtb290aCB0aGUgZHJvcG91dCBub2lzZSBpbiB0aGUgZ2VuZSBzY29yZXMKCmBgYHtyfQpwcm9qIDwtIGFkZEltcHV0ZVdlaWdodHMocHJvaikKYGBgCgpgYGB7cn0KbWFya2VyR2VuZXMgIDwtIGMoCiAgICAiQ0QzNCIsICAjRWFybHkgUHJvZ2VuaXRvcgogICAgIkdBVEExIiwgI0VyeXRocm9pZAogICAgIlBBWDUiLCAiTVM0QTEiLCAiTU1FIiwgI0ItQ2VsbCBUcmFqZWN0b3J5CiAgICAiQ0QxNCIsICJNUE8iLCAjTW9ub2N5dGVzCiAgICAiQ0QzRCIsICJDRDhBIiNUQ2VsbHMKICApCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KcCA8LSBwbG90RW1iZWRkaW5nKAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICBjb2xvckJ5ID0gIkdlbmVTY29yZU1hdHJpeCIsIAogICAgbmFtZSA9IG1hcmtlckdlbmVzLCAKICAgIGVtYmVkZGluZyA9ICJVTUFQIiwKICAgIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKZG8uY2FsbChncmlkRXh0cmE6OmdyaWQuYXJyYW5nZSwgYyhwLCBuY29sID0gMykpCmBgYAoKCiMgVmlzdWFsaXppbmcgR2Vub21lIEJyb3dzZXIgVHJhY2tzCgpCcm93c2UgbG9jYWwgY2hyb21hdGluIGFjY2Vzc2liaWxpdHkgYXQgbWFya2VyIGdlbmVzLiBQbG90IGdlbm9tZQpicm93c2VyIHRyYWNrcyBwZXIgY2x1c3RlcgpgYGB7cn0KcCA8LSBwbG90QnJvd3NlclRyYWNrKAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICBncm91cEJ5ID0gIkNsdXN0ZXJzIiwgCiAgICBnZW5lU3ltYm9sID0gbWFya2VyR2VuZXMsIAogICAgdXBzdHJlYW0gPSA1MDAwMCwKICAgIGRvd25zdHJlYW0gPSA1MDAwMAopCmBgYAoKCmBgYHtyfQpncmlkOjpncmlkLm5ld3BhZ2UoKQpncmlkOjpncmlkLmRyYXcocCRDRDE0KQpgYGAKCgoKIyBJbnRlZ3JhdGlvbiB3aXRoIHNjUk5BLXNlcQoKKiB0aGUgc2NBVEFDLXNlcSBnZW5lIHNjb3JlIG1hdHJpeCBpcyBjb21wYXJlZCB3aXRoIHRoZSBzY1JOQS1zZXEgZ2VuZSBleHByZXNzaW9uCm1hdHJpeAoqIGZvciB0aGlzIGFsaWdubWVudCB0aGUgYEZpbmRUcmFuc2ZlckFuY2hvcnMoKWAgZnVuY3Rpb24gZnJvbSBTZXVyYXQgaXMgdXNlZAoqIHRvIHNjYWxlIGZvciBsYXJnZSBzYW1wbGUgc2l6ZSwgdGhpcyBwcm9jZXNzIGlzIHBhcmFsbGVsaXplZAoqIGZvciBlYWNoIGNlbGwgaW4gQVRBQyB3ZSBmaW5kIHRoZSBjZWxsIGluIHNjUk5BLXNlcSB0aGF0IGxvb2tzIG1vc3Qgc2ltaWxhciAKLT4gYXNzaWduIHRoZSBjb3JyZXBzb25kaW5nIGdlbmUgZXhwcmVzc2lvbiB0byB0aGF0IGNlbGwuIAoKQXBhcnQgZnJvbSB1c2luZyB0aGlzIGluZm9ybWF0aW9uIGZvciBpZGVudGlmeWluZyBjbHVzdGVycyB3ZSBjYW4gYWxzbyB1c2UgaXQgCmZvciBpZGVudGlmeWluZyBwcmVkaWN0ZWQgY2lzLXJlZ3VsYXRvcnkgZWxlbWVudHMuCgoKYGBge3J9CmlmKCFmaWxlLmV4aXN0cygic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiKSl7CiAgICBkb3dubG9hZC5maWxlKAogICAgICAgIHVybCA9ICJodHRwczovL2plZmZncmFuamEuczMuYW1hem9uYXdzLmNvbS9BcmNoUi9UZXN0RGF0YS9zY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIsCiAgICAgICAgZGVzdGZpbGUgPSAic2NSTkEtSGVtYXRvcG9pZXNpcy1HcmFuamEtMjAxOS5yZHMiCiAgICApCn0KCiMgcmFuZ2VkIHN1bW1hcml6ZWQgRXhwZXJpbWVudApzZVJOQSA8LSByZWFkUkRTKCJzY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIpCnNlUk5BCmBgYAoKTGV0cyBoYXZlIGEgbG9vayBhdCB0aGUgY291bnQgbWF0cml4OgoKYGBge3J9CiMgc3BhcnNlIGNvdW50IG1hdHJpeCBvZiBzY1JOQS1zZXEKYXNzYXlzKHNlUk5BKVtbMV1dWzE6MTAsIDE6NV0KYGBgCgpNZXRhZGF0YSBvZiB0aGUgc2NSTkEtc2VxIGRhdGFzZXQ6CgpXZSBhbHJlYWR5IGhhdmUgY2x1c3RlcmluZywgdW1hcCBlbWJlZGRpbmdzIGFuZCBjZWxsIHR5cGVzLgoKYGBge3IsIHJlc3VsdHMgPSAiYXNpcyJ9CmNvbERhdGEoc2VSTkEpICU+JSBoZWFkICU+JSBrbml0cjo6a2FibGUoKQpgYGAKClBsb3QgUXVhbGl0eSBNZXRyaWNzIG9mIHRoZSBzY1JOQS1zZXEgZGF0YXNldDoKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmFzX2RhdGFfZnJhbWUoY29sRGF0YShzZVJOQSkpICU+JSAKICBzZWxlY3QoblVNSSwgbkdlbmUsIEdyb3VwKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAhR3JvdXAsIG5hbWVzX3RvID0gInN0YXQiKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fdmlvbGluKGFlcyh4ID0gR3JvdXAsIHkgPSB2YWx1ZSwgZmlsbCA9IEdyb3VwKSkgKwogIGZhY2V0X3dyYXAofnN0YXQsIHNjYWxlcyA9ICJmcmVlIikgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgeGxhYigiU2FtcGxlIikKICAjcGl2b3RfbG9uZ2VyKGNvbHMgPSAhcykKYGBgCgoKYGBge3IsIGZpZy53aWR0aD04LCBmaWcuaGVpZ2h0PTEwfQpkZiA8LSBhc19kYXRhX2ZyYW1lKGNvbERhdGEoc2VSTkEpKQoKcDEgPC0gZ2dwbG90KCkgKwpnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoIlVNQVAxIiksIHkgPSBkZiAlPiUgcHVsbCgiVU1BUDIiKSwgCiAgICAgICAgICAgICAgIGNvbCA9IGRmICU+JSBwdWxsKCJCaW9DbGFzc2lmaWNhdGlvbiIpKSwgc2l6ZSA9IC4wNCkgKwogIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPSJDZWxsVHlwZSIpKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJzY1JOQS1zZXEgZGF0YXNldCAtIGNlbGwgdHlwZSIpCgpwMiA8LSBnZ3Bsb3QoKSArCmdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgiVU1BUDEiKSwgeSA9IGRmICU+JSBwdWxsKCJVTUFQMiIpLCAKICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwoIm5HZW5lIikpLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9Im51bWJlciBvZiBnZW5lcyIpKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIHhsYWIoInVtYXAxIikgKwogIHlsYWIoInVtcGEyIikgKwogIGxhYnModGl0bGUgPSAic2NSTkEtc2VxIGRhdGFzZXQgLSBnZW5lIG51bWJlciIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIG5jb2wgPSAxKQpgYGA=